// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/win/scoped_process_information.h"

#include "base/logging.h"
#include "base/win/scoped_handle.h"

namespace base {
namespace win {

    namespace {

        // Duplicates source into target, returning true upon success. |target| is
        // guaranteed to be untouched in case of failure. Succeeds with no side-effects
        // if source is NULL.
        bool CheckAndDuplicateHandle(HANDLE source, ScopedHandle* target)
        {
            if (!source)
                return true;

            HANDLE temp = NULL;
            if (!::DuplicateHandle(::GetCurrentProcess(), source,
                    ::GetCurrentProcess(), &temp, 0, FALSE,
                    DUPLICATE_SAME_ACCESS)) {
                DWORD last_error = ::GetLastError();
                DPLOG(ERROR) << "Failed to duplicate a handle " << last_error;
                ::SetLastError(last_error);
                return false;
            }
            target->Set(temp);
            return true;
        }

    } // namespace

    ScopedProcessInformation::ScopedProcessInformation()
        : process_id_(0)
        , thread_id_(0)
    {
    }

    ScopedProcessInformation::ScopedProcessInformation(
        const PROCESS_INFORMATION& process_info)
        : process_id_(0)
        , thread_id_(0)
    {
        Set(process_info);
    }

    ScopedProcessInformation::~ScopedProcessInformation()
    {
        Close();
    }

    bool ScopedProcessInformation::IsValid() const
    {
        return process_id_ || process_handle_.Get() || thread_id_ || thread_handle_.Get();
    }

    void ScopedProcessInformation::Close()
    {
        process_handle_.Close();
        thread_handle_.Close();
        process_id_ = 0;
        thread_id_ = 0;
    }

    void ScopedProcessInformation::Set(const PROCESS_INFORMATION& process_info)
    {
        if (IsValid())
            Close();

        process_handle_.Set(process_info.hProcess);
        thread_handle_.Set(process_info.hThread);
        process_id_ = process_info.dwProcessId;
        thread_id_ = process_info.dwThreadId;
    }

    bool ScopedProcessInformation::DuplicateFrom(
        const ScopedProcessInformation& other)
    {
        DCHECK(!IsValid()) << "target ScopedProcessInformation must be NULL";
        DCHECK(other.IsValid()) << "source ScopedProcessInformation must be valid";

        if (CheckAndDuplicateHandle(other.process_handle(), &process_handle_) && CheckAndDuplicateHandle(other.thread_handle(), &thread_handle_)) {
            process_id_ = other.process_id();
            thread_id_ = other.thread_id();
            return true;
        }

        return false;
    }

    PROCESS_INFORMATION ScopedProcessInformation::Take()
    {
        PROCESS_INFORMATION process_information = {};
        process_information.hProcess = process_handle_.Take();
        process_information.hThread = thread_handle_.Take();
        process_information.dwProcessId = process_id();
        process_information.dwThreadId = thread_id();
        process_id_ = 0;
        thread_id_ = 0;

        return process_information;
    }

    HANDLE ScopedProcessInformation::TakeProcessHandle()
    {
        process_id_ = 0;
        return process_handle_.Take();
    }

    HANDLE ScopedProcessInformation::TakeThreadHandle()
    {
        thread_id_ = 0;
        return thread_handle_.Take();
    }

} // namespace win
} // namespace base
